import React, { useReducer, useContext } from 'react';
import { Input, Icon, Modal } from 'antd';
import classNames from 'classnames';
import { useDeepCompareEffect } from 'ahooks';
import Tree, { FilteringContainer, renderers } from 'react-virtualized-tree';
import get from 'lodash/get';
import last from 'lodash/last';
import { has, isEqual } from 'lodash';
import { mapNodes } from '@/utils/utils';
import { contains, TreeFilter, serialize2 } from './utils';
import styles from './styles.less';
import PathInput, { renderPath } from '@/components/PathInput';

const { Expandable } = renderers;

const initialState = {
  visible: false,
  treeData: [],
  value: [],
};

function reducer(state, action) {
  const { type, payload } = action;
  if (type === 'sync_props') {
    const newState = {
      ...state,
      ...payload,
    };
    const { value, treeData } = newState;

    if (value === null) {
      return {
        ...state,
        treeData: mapNodes(treeData, k => {
          if (has(k, 'state.selected')) {
            delete k.state.selected;
          }
          return k;
        }),
        value: null,
      };
    }

    const d = serialize2(treeData, value);

    return {
      ...newState,
      treeData: d,
    };
  }

  if (type === 'update_tree') {
    return {
      ...state,
      ...payload,
    };
  }

  if (type === 'clear') {
    return {
      ...state,
      treeData: mapNodes(state.treeData, k => {
        delete k.state.selected;
        return k;
      }),
      value: [],
    };
  }

  if (type === 'hide') {
    return {
      ...state,
      visible: false,
    };
  }

  if (type === 'show') {
    return {
      ...state,
      visible: true,
    };
  }

  if (type === 'select_this_node') {
    const { current } = payload;

    return {
      ...state,
      treeData: serialize2(state.treeData, current.nodePath),
      visible: false,
      current,
      value: current.nodePath,
    };
  }

  throw new Error();
}

function NodePathSelect({
  value = null,
  disabled = false,
  readOnly = false,
  onChange,
  className = '',
  style = {},
  mode = 'select',
  treeData,
}) {
  const [state, dispatch] = useReducer(reducer, initialState);

  useDeepCompareEffect(() => {
    if (Array.isArray(treeData)) {
      dispatch({ type: 'sync_props', payload: { treeData } });
    }
  }, [treeData]);

  const getParentNodeType = (nodes, parentId) => {
    let parent;
    mapNodes(nodes, k => {
      if (k.id === parentId) {
        parent = k;
      }
      return k;
    });

    return get(parent, 'nodeType');
  };

  useDeepCompareEffect(() => {
    dispatch({ type: 'sync_props', payload: { value } });
  }, [value]);

  if (mode === 'select') {
    return (
      <>
        <Input
          suffix={<Icon type="select" />}
          placeholder="选择路径"
          value={renderPath(value)}
          onClick={e => {
            e.preventDefault();
            dispatch({ type: 'show' });
          }}
          onChange={e => {
            if (e.target.value === '') {
              onChange(null);
            }
          }}
          allowClear
          className={className}
          style={style}
          readOnly={readOnly}
          disabled={disabled}
        />

        {state.visible && (
          <Modal
            title="选择节点路径"
            destroyOnClose={false}
            width={1000}
            visible={state.visible}
            onCancel={() => dispatch({ type: 'hide' })}
            footer={null}
          >
            <FilteringContainer nodes={state.treeData} indexSearch={TreeFilter}>
              {({ nodes }) => (
                <ul className={styles.wtree} style={{ height: 300 }}>
                  <Tree
                    nodes={nodes}
                    onChange={nodes => {
                      dispatch({ type: 'update_tree', payload: { treeData: nodes } });
                    }}
                  >
                    {({ style, node, ...rest }) => {
                      const parentIsArray =
                        getParentNodeType(state.treeData, last(get(node, 'parents'))) === 'Array';
                      return (
                        <div
                          style={{ ...style, paddingLeft: style.marginLeft + 24, marginLeft: 0 }}
                          className={classNames(styles.row, {
                            [styles.selected]: isEqual(state.value, node.nodePath),
                          })}
                        >
                          <Expandable
                            node={node}
                            parentIsArray={parentIsArray}
                            {...rest}
                            iconsClassNameMap={{
                              expanded: `tgIcon icon-arrow-down ${styles.switcher}`,
                              collapsed: `tgIcon icon-arrow-right ${styles.switcher}`,
                              // lastChild: 'mi mi-insert-drive-file',
                            }}
                          >
                            <span
                              className={styles.node}
                              style={{
                                width: `calc(100% - ${style.marginLeft}px)`,
                                cursor: 'pointer',
                              }}
                              onClick={() => {
                                dispatch({ type: 'select_this_node', payload: { current: node } });
                                onChange(node.nodePath);
                              }}
                            >
                              <i
                                className={classNames(
                                  'tgIcon',
                                  `icon-${node.nodeType}`.toLocaleLowerCase(),
                                  styles.icon,
                                  styles[`${node.nodeType}`.toLocaleLowerCase()]
                                )}
                              />
                              {node.nodeName}
                            </span>
                          </Expandable>
                        </div>
                      );
                    }}
                  </Tree>
                </ul>
              )}
            </FilteringContainer>
          </Modal>
        )}
      </>
    );
  }

  return (
    <PathInput
      className={className}
      value={value}
      onChange={onChange}
      placeholder="输入路径，如：$['root']['request']['orderId']"
    />
  );
}

export default NodePathSelect;
